﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Argos.Scheduling
{
	/// <summary>
	/// Represents an occurrence in a schedule.
	/// </summary>
	[Serializable]
	public sealed class Occurrence : IEquatable<Occurrence> , IComparable , IComparable<Occurrence>
	{

		#region #ctor: public Occurrence(DateTime start)

		/// <summary>
		/// Creates a new instance of the occurrence.
		/// </summary>
		/// <param name="start">The start date and time of the occurrence.</param>
		public Occurrence(DateTime start) : this(start, TimeSpan.Zero) { }

		#endregion

		#region #ctor: public Occurrence(DateTime start, TimeSpan duration)

		/// <summary>
		/// Creates a new instance of the occurrence.
		/// </summary>
		/// <param name="start">The start date and time of the occurrence.</param>
		/// <param name="duration">The duration of the occurrence.</param>
		public Occurrence(DateTime start, TimeSpan duration)
		{
			this.Start = start;
			this.Duration = duration;
		}

		#endregion

		#region #ctor: public Occurrence(DateTime start, DateTime end)

		/// <summary>
		/// Creates a new instance of the class and specifies the start
		/// and end of the occurrence.
		/// </summary>
		/// <param name="start">The start date and time of the occurrence.</param>
		/// <param name="end">The end date and time of the occurrence.</param>
		/// <exception cref="ArgumentException">The exception that is thrown if end is less than start.</exception>
		public Occurrence(DateTime start, DateTime end)
		{
			if (end < start)
			{
				throw new ArgumentException("End date and time cannot be less than start");
			}

			this.Start = start;
			this.Duration = end.Subtract(start);
		}

		#endregion		
		


		#region public TimeSpan Duration { get; private set; }

		/// <summary>
		/// Returns the duration of the occurrence.
		/// </summary>
		public TimeSpan Duration { get; private set; }

		#endregion

		#region public DateTime End { get; }

		/// <summary>
		/// Returns the end date and time of the occurrence.
		/// </summary>
		public DateTime End
		{
			get { return this.Start.Add(this.Duration); }
		}

		#endregion

		#region public TimeOfDay EndTime { get; }

		/// <summary>
		/// Returns the time of the end of the occurrence.
		/// </summary>
		public TimeOfDay EndTime
		{
			get { return new TimeOfDay(this.End); }
		}

		#endregion
		
		#region public DateTime Start { get; private set; }

		/// <summary>
		/// Returns the date and time of the start of the occurrence.
		/// </summary>
		public DateTime Start { get; private set; }
		
		#endregion

		#region public TimeOfDay StartTime { get; }

		/// <summary>
		/// Returns the time of the start of the occurrence.
		/// </summary>
		public TimeOfDay StartTime
		{
			get { return new TimeOfDay(this.Start); }
		}

		#endregion



		#region public int CompareTo(object obj)

		/// <summary>
		/// Compares the given object to the current occurrence.
		/// </summary>
		/// <param name="obj">The object to compare to the current.</param>
		public int CompareTo(object obj)
		{
			if (!(obj is Occurrence))
			{
				throw new ArgumentException("Object must be an instance of 'Occurrence'.", "obj");
			}

			return this.CompareTo((Occurrence)obj);
		}

		#endregion

		#region public int CompareTo(Occurrence other)

		/// <summary>
		/// Compares the current instance to the given.
		/// </summary>
		/// <param name="other">The occurrence to compare to the current.</param>
		public int CompareTo(Occurrence other)
		{
			if (null == other)
			{
				return 1;
			}

			string sThis = this.Duration == TimeSpan.Zero ? string.Format("{0:u}", this.Start) : string.Format("{0:u}-{1}", this.Start, this.Duration);
			string sThat = other.Duration == TimeSpan.Zero ? string.Format("{0:u}", other.Start) : string.Format("{0:u}-{1}", other.Start, other.Duration);

			return string.Compare(sThis, sThat);
		}

		#endregion
		
		#region public override bool Equals(object obj)

		/// <summary>
		/// Compares the current instance to the given object.
		/// </summary>
		/// <param name="obj">The object to compare the current instance to.</param>
		public override bool Equals(object obj)
		{
			if (obj is Occurrence)
			{
				return this.Equals((Occurrence)obj);
			}

			return false;
		}

		#endregion

		#region public bool Equals(Occurrence other)

		/// <summary>
		/// Compares the current instance to the given occurrence instance.
		/// </summary>
		/// <param name="other">The occurrence to compare to the current.</param>
		public bool Equals(Occurrence other)
		{
			return string.Equals(this.ToString(), other.ToString());
		}

		#endregion

		#region public override int GetHashCode()

		/// <summary>
		/// Returns the hash code for the current instance.
		/// </summary>
		public override int GetHashCode()
		{
			return this.ToString().GetHashCode();
		}

		#endregion
		
		#region public override string ToString()

		/// <summary>
		/// Returns the string representation of the occurrence.
		/// </summary>
		public override string ToString()
		{
			if (this.Duration == TimeSpan.Zero)
			{
				return this.Start.ToString();
			}
			return string.Format("{0} to {1}", this.Start, this.End);
		}

		#endregion
		
	}
}
